package com.atguigu.gmall.index.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.scheduling.support.SimpleTriggerContext;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.Timer;
import java.util.TimerTask;

@Component
//@Scope("prototype")
public class DistributedLock {
    @Autowired
    StringRedisTemplate redisTemplate;

    private Timer timer;
    /**
     * 为目标方法提供分布式锁的加锁方法
     * @return
     */
    public Boolean tryLock(String lockName,String uuid,Long expireTime){
        String script = "if redis.call('EXISTS',KEYS[1])==0 " +
                "or redis.call('HEXISTS',KEYS[1],ARGV[1])==1 " +
                "then redis.call('HINCRBY',KEYS[1],ARGV[1],1) " +
                "redis.call('expire',KEYS[1],ARGV[2]) " +
                "return 1 else return 0 end";

        Boolean result = this.redisTemplate.execute(new DefaultRedisScript<>(script, Boolean.class), Arrays.asList(lockName), uuid, expireTime.toString());
        if (!result){
            try {
                Thread.sleep(80);
            tryLock(lockName, uuid, expireTime);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }else {
            this.reNewLock(lockName,uuid,expireTime.intValue());
        }
        return true;
    }

    /**
     * 为目标方法提供解锁的方法
     */
    public void unlock(String lockName,String uuid){
        String script = "if redis.call('HEXISTS',KEYS[1],ARGV[1])==0 then return nil elseif redis.call('HINCRBY',KEYS[1],ARGV[1],-1)==0 then return redis.call('DEL',KEYS[1]) else return 0 end";
        Long result = this.redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Arrays.asList(lockName), uuid);
        if (result==null){
            throw new RuntimeException("你正常尝试解别人的锁，此行为严重涉及安全，请尽快停止");
        }else if (result==1){
            this.timer.cancel();
        }

    }


    /**
     * 为锁自动续期的方法
     */
    public void reNewLock(String lockName, String uuid,Integer expireTime){
        String script = " if redis.call('HEXISTS',KEYS[1],ARGV[1])==1  " +
                "then redis.call('expire',KEYS[1],ARGV[2]) " +
                "return 1 else  return 0 end";
        timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
        redisTemplate.execute(new DefaultRedisScript<>(script,Boolean.class),Arrays.asList(lockName),uuid,expireTime.toString());
            }
        },expireTime*1000/3,expireTime*1000/3);

    }

}

